//package game;
package processing.test.mechaapp;

import processing.core.*;
import processing.test.mechaapp.Atom;

//import processing.data.*; 
import java.util.Random;
import java.util.ArrayList;
import java.util.Iterator;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;

import android.media.*;
import android.content.res.*;
import android.content.Context;
import android.os.Looper;
import android.os.Vibrator;
import android.view.GestureDetector;
import android.view.MotionEvent;

public class MechaApp extends PApplet {

	// Play grid
	Atom grid[][];

	// Scoring Variables
	int CurScore;
	int HiScore;
	int CurBonus;
	int deductions;

	// Player Log file
	PrintWriter log = null;

	// High Scores file
	String[] scores;
	String[] reference;
	File file;
	FileOutputStream fos;
	int i;

	// Menu states variables
	final int STATE_WELCOME = 0;
	final int STATE_TIMED = 1;
	final int STATE_STRATEGY = 2;
	final int STATE_INSTRUCTIONS = 3;
	final int STATE_INSTRUCTIONS2 = 7;
	final int STATE_INSTRUCTIONS3 =8;
	final int STATE_HIGHSCORE = 4;
	final int STATE_PAUSED = 5;
	final int STATE_GAMEOVER = 6;
	

	// Images
	PImage background;
	PImage timedButton;
	PImage strategyButton;
	PImage instructButton;
	PImage highscoreButton;
	PImage gameBackground;
	PImage mainmenuButton;
	PImage pauseButton;
	PImage pauseBackground;
	PImage gameoverBackground;
	PImage hiscoresButton;
	PImage hydrogen; // 1 bond
	PImage oxygen; // 2 bonds
	PImage nitrogen; // 3 bonds
	PImage carbon; // 4 bonds
	PImage instructions1; //instructions for the game
	PImage instructions2; // instructions 2nd page for the game
	PImage instructions3;
	PImage details;
	PImage backToHQ;
	PImage instructionsBoard;
	


	// State of the program; Initialized at STATE_WELCOME
	int state = STATE_WELCOME;
	int prevState;

	// Timer used for timing game modes
	Timer timer;

	// Starting time limit set for timed mode; Default is 2 minutes
	private final int LIMIT = 120000;

	// Amount of time added when molecules are completed; Default is 10 seconds
	private final int ADDTIME = 10000;

	// Number of columns and rows in the grid
	private final int cols = 7;
	private int rows = 7;

	//Oxygen, Carbon, Nitrogen, Hydrogen, Blank atoms
	int Ox, Ca, Ni, Hy, Bk;

	float sz;// size of the atoms is declared.
	
	//from Random class, rand is created to have random atoms onto the grid.
	Random rand = new Random();
	Atom selectedAtom = null;

	// Chance of spawning atoms regarding number of moves made
	int spawnAtom;
	int numOfMoves;
	int numAtoms = 0;
	int oldNumAtoms = 0;

	// Molecule history array
	private ArrayList<Molecule> history;

	// ANDROID Gestures
	GestureDetector listener;
	GestureListener context;

	// Music player
	MediaPlayer main = new MediaPlayer();
	MediaPlayer timed = new MediaPlayer();
	MediaPlayer strategy = new MediaPlayer();
	MediaPlayer over = new MediaPlayer();
	boolean isPaused = false;

	// ANDROID Vibrate
	// Must enable in manifest:
	// <uses-permission android:name="android.permission.VIBRATE"/>
	Vibrator vibrate;

	//Initial game "properties such as screen size, background color, loading images etc." 
	public void setup() {
		orientation(PORTRAIT); // for ANDROID
		smooth();

		// Required to use Processing's API
		Atom.setApplet(this);
		Timer.setApplet(this);

		sz = (width / cols); //size of each invisible box where the atom will be placed in.
		Atom.size = (float) (sz / (1.4));//size of atoms are initialized.

		// Creates timer for timed modes
		timer = new Timer(LIMIT, width / 2, 575);

		// A new PlayerLogt.txt is created each time the app is run
		// This is avoid the risk of creating too large a file
		initLog("PlayerLog.txt");

		// ANDROID Gestures: single tap, swipe, double tap
		Looper.prepare();
		context = new GestureListener(this);
		context.setSingleTap("singleTap");
		context.setSwipe("swipe");
		context.setDoubleTap("doubleTap");
		listener = new GestureDetector(context);

		// ANDROID Vibrate
		vibrate = (Vibrator) this.getSystemService(Context.VIBRATOR_SERVICE);

		// Load Images
		background = loadImage("welcome_bg.bmp");
		timedButton = loadImage("timed_button.png");
		strategyButton = loadImage("strategy_button.png");
		instructButton = loadImage("instructions_button.png");
		highscoreButton = loadImage("highscores_button.png");
		gameBackground = loadImage("gameMode_bg.bmp");
		mainmenuButton = loadImage("mainmenu_button.bmp");
		pauseButton = loadImage("pause_button.bmp");
		pauseBackground = loadImage("pause_bg.bmp");
		gameoverBackground = loadImage("gameover_bg.bmp");
		hiscoresButton = loadImage("hiscores_button.bmp");
		hydrogen = loadImage("mol1.png"); // 1
		oxygen = loadImage("mol2.png");// 2
		nitrogen = loadImage("mol3.png");// 3
		carbon = loadImage("mol4.png"); // 4
		instructions1 = loadImage("i1.png");
		instructions2 = loadImage("i2.png");
		details = loadImage("details_button.png");
		backToHQ = loadImage("back_button.png");
		instructionsBoard = loadImage("instructions.png");
		instructions3 = loadImage("I3.png");

		
		//resizing the atoms.
		hydrogen.resize(0, (int) Atom.size + 7);
		oxygen.resize(0, (int) Atom.size + 7);
		nitrogen.resize(0, (int) Atom.size + 7);
		carbon.resize(0, (int) Atom.size + 7);
		
		//setting the appropriate image for each atom.
		Hydrogen.setImage(hydrogen);//1 bond
		Oxygen.setImage(oxygen);// 2 bonds
		Nitrogen.setImage(nitrogen);// 3 bonds
		Carbon.setImage(carbon); // 4 bonds
		
		// Loads High Scores, and updates class variables
		/*scores = loadStrings("SCORES");
		reference = loadStrings("SCORES");
		System.out.println("Hi Score is ");
		for (i = 0; i < scores.length; i++) {
		System.out.println(scores[i]);
		}
		String s1 = scores[0];
		HiScore = Integer.parseInt(s1);*/
				
		//reference the Scores.txt file in android internal memory
		file = getBaseContext().getFileStreamPath("Scores.txt");
		scores = new String[10];
		try{	
			//check if the file already exist
			//if so, then load from it
			if(file.exists()){
		    	System.out.println("\"Scores.txt\" file exist");
				
				FileReader fr = new FileReader(file);
				BufferedReader reader = new BufferedReader(fr);
			
				String buffer = reader.readLine();
				
			for(int k=0; buffer != null && k<scores.length; k++){
				scores[k] = buffer;
				System.out.println("line read: "+buffer);
				HiScore = parseInt(buffer);//set the lowest high score
				buffer = reader.readLine();
			}
						
				reader.close();
				fr.close();
				}
			//if the file does not exist, then set all scores to 0
			else{
				for(i=0; i<scores.length; i++)
				scores[i] = "0";
				HiScore = 0;
			}
				
			//this overrides the Scores.txt file or creates a new file if
			//it does not already exist
			fos = openFileOutput("Scores.txt", Context.MODE_PRIVATE);
				
			}catch (FileNotFoundException e1) {
				e1.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

			//copy the scores into the newly created Scores.txt file
		for(i=0; i<scores.length; i++)
			try {
				fos.write((scores[i]+"\n").getBytes());
				System.out.println("wrote to file: "+scores[i]);
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				fos.flush();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				
			//print whats in scores
			for(int k=0; k<scores.length; k++)
				System.out.println(scores[k]);
				
		// Resizes Background Images
		background.resize(width, height);
		gameBackground.resize(width, height);
		pauseBackground.resize(width, height);
		gameoverBackground.resize(width, height);

		// Enables and runs music upon startup
		try {
			AssetManager assets = this.getAssets();
			AssetFileDescriptor fd = assets.openFd("heroic-struggle.mp3");
			main.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(),
					fd.getLength());
			main.setLooping(true);
			main.prepare();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		try {
			AssetManager assets = this.getAssets();
			AssetFileDescriptor fd = assets.openFd("dark-skies-of-doom.mp3");
			timed.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(),
					fd.getLength());
			timed.setLooping(true);
			timed.prepare();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			AssetManager assets = this.getAssets();
			AssetFileDescriptor fd = assets.openFd("reflections.mp3");
			strategy.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(),
					fd.getLength());
			strategy.setLooping(true);
			strategy.prepare();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		try {
			AssetManager assets = this.getAssets();
			AssetFileDescriptor fd = assets.openFd("spring-to-life.mp3");
			over.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(),
					fd.getLength());
			over.setLooping(true);
			over.prepare();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		

		System.out.println("Setup is completed");
	}

	public void draw() {
		// Welcome Screen
		if (state == STATE_WELCOME) {
			if(timed.isPlaying()){
				timed.pause();
			}
			if(strategy.isPlaying()){
				strategy.pause();
			}
			if(over.isPlaying()){
				over.pause();
			}
			main.start();
			//background set to background image
			//Buttons are located with x and y coordinates.
			background(background);
			image(timedButton, 15, 17);
			image(strategyButton, 243, 128);
			image(instructButton, 15, 437);
			image(highscoreButton, 243, 554);

			// DO NOT REMOVE
			// Music creator MUST be credited (http://incompetech.com)
			PFont font;
			font = loadFont("Roboto-Bold-48.vlw");
			fill(255);
			textFont(font);
			textSize(10);
			textAlign(PConstants.LEFT);
			text("Music: freestockmusic.com", 3, height - 3);
		}
		// Timed Mode (hard mode)
		//setting the background, timer, atoms onto the grid
		else if (state == STATE_TIMED) {
			if(main.isPlaying()){
			main.pause();
			}
			timed.start();
			background(gameBackground);
			image(mainmenuButton, 260, 706);
			image(pauseButton, 20, 706);
			timer.display();
			if (grid != null) {
				for (int i = 0; i < cols; i++) {
					for (int j = 0; j < rows; j++) {
						grid[i][j].drawLines();
					}
				}
				//drawing atoms onto grid
				for (int i = 0; i < cols; i++) {
					for (int j = 0; j < rows; j++) {
						grid[i][j].drawMe();
					}
				}
			}
			refreshScores();

			// End Condition-Game Over! timer ran out.
			if (timer.getTime() <= 0) {
				grid = null;
				history = null;
				System.gc(); // garbage collection flag

				printToLog("   [EVENT] Timer has reached 0");

				state = STATE_GAMEOVER;
				System.out.println("--------------GAMEOVER--------------");
				System.out.println("Score: " + (CurScore - CurBonus)
						+ "\nBonus: " + CurBonus + "\nDeductions: "
						+ deductions);

				printToLog("   [GAME OVER] Time: " + timer.getTime());
				printToLog("               Score: " + (CurScore - CurBonus)
						+ "\n               Bonus: " + CurBonus
						+ "\n               Deductions: " + deductions);

				CurScore -= deductions;
				//if statement saving new high score based on last one.
				if (CurScore >= HiScore) {
					HiScore = CurScore;
					printToLog("   [NEW HIGH SCORE] " + HiScore);
					SaveScores(HiScore);
				}
			}
		}
		// Strategy Mode (easy mode)
		//setting the background, timer, atoms blocks onto the grid
		else if (state == STATE_STRATEGY) {
			if(main.isPlaying()){
				main.pause();
			}
			strategy.start();
			background(gameBackground);
			image(mainmenuButton, 260, 706);
			image(pauseButton, 20, 706);
			timer.display();
			if (grid != null) {
				for (int i = 0; i < cols; i++) {
					for (int j = 0; j < rows; j++) {
						grid[i][j].drawLines();
					}
				}
				for (int i = 0; i < cols; i++) {
					for (int j = 0; j < rows; j++) {
						grid[i][j].drawMe();
					}
				}
			}
			refreshScores();
		}
		// Instructions Screen
		else if (state == STATE_INSTRUCTIONS) {
			background(instructions1);
			image(details, 61, 644);
			image(backToHQ, 147, 724);
		}
		// Instructions Screen Part 2
		else if (state == STATE_INSTRUCTIONS2) {
			background(instructions2);
			image(details, 62, 680);
		}
		//Instructions screen Par 3
		else if (state == STATE_INSTRUCTIONS3){
			background(instructions3);
			image(backToHQ, 147, 724);
		}
		// High Score Screen
		else if (state == STATE_HIGHSCORE) {
			if(main.isPlaying()){
				main.pause();
			}
			over.start();
			background(instructionsBoard);
			PFont font;
			font = loadFont("zap.vlw");
			fill(255);
			textFont(font, 22);
			textAlign(PConstants.LEFT);
			for (int i = 0; i < 10; i++) {
				int temp = i + 1;
				String temp2;
				if (i == 0) {
					temp2 = "Score " + temp + ":                      "
							+ this.scores[i];
				} else if (i == 9) {
					temp2 = "Score " + temp + ":                   "
							+ this.scores[i];
				} else {
					temp2 = "Score " + temp + ":                    "
							+ this.scores[i];
				}
				text(temp2, 100, 200 + (i * 50));
			}

			image(backToHQ, 147, 724);
		}
		// Pause Screen
		else if (state == STATE_PAUSED) {
			background(pauseBackground);
			if(timed.isPlaying()){
				timed.pause();
			}
			if(strategy.isPlaying()){
				strategy.pause();
			}
		}
		// Game Over Screen
		else if (state == STATE_GAMEOVER) {
			if(timed.isPlaying()){
				timed.pause();
			}
			if(strategy.isPlaying()){
				strategy.pause();
			}
			over.start();
			background(gameoverBackground);
			image(mainmenuButton, 260, 463);
			image(hiscoresButton, 20, 463);

			PFont font;
			font = loadFont("Roboto-Bold-48.vlw");
			fill(255);
			textFont(font);
			textAlign(PConstants.CENTER);
			text(CurScore, width / 2, 439);
		}
	}

	// ANDROID
	// Fired when screen is first touched, then fires onTouchEvent
	// ------not continuous-----
	public boolean surfaceTouchEvent(MotionEvent event) {
		mouseX = (int) event.getX();
		mouseY = (int) event.getY();

		// Welcome Screen mouseClicked Event
		if (state == STATE_WELCOME) {
			// Timed Mode Button
			if (atomOver(30 + (199 / 2), 30 + (199 / 2), 199)) {
				printToLog("   [START] Timed Mode; Timer starts at "
						+ ((LIMIT / 1000.0) / 60) + " minutes");
				state = STATE_TIMED;

				history = new ArrayList<Molecule>();
				spawnAtom = 12;
				numOfMoves = CurScore = CurBonus = deductions = numAtoms = 0;
				grid = new Atom[cols][rows];
				//setting the atoms onto the grid, 
				//the numbers represent the probability of the atoms onto the grid.
				Ox = 45;
				Ca = 15;
				Ni = 30;
				Hy = 70;
				Bk = 150;
				for (int i = 0; i < cols; i++) {
					for (int j = 0; j < rows; j++) {
						//spawn new atom based on i,j position. 
						//Spawn from the atoms:Blank atom,hydrogen,oxygen,nitrogen,carbon
						spawnNewAtom(i, j, Spawn(Bk, Hy, Ox, Ni, Ca));
					}
				}
				Ox = 45;
				Ca = 15;
				Ni = 30;
				Hy = 180;
				
				//setting timer to Countdown in hard mode screen.
				timer.setDirection(Timer.COUNTDOWN);
				timer.start(LIMIT);
				redraw();
			}
			// Strategy Mode Button (easy mode)
			if (atomOver(257 + (199 / 2), 141 + (199 / 2), 199)) {
				printToLog("   [START] Strategy Mode");
				state = STATE_STRATEGY;

				history = new ArrayList<Molecule>();
				spawnAtom = 10;
				numOfMoves = CurScore = CurBonus = deductions = numAtoms = oldNumAtoms = 0;
				grid = new Atom[cols][rows];
				//setting the atoms onto the grid, 
				//the numbers represent the probability of the atoms onto the grid.
				Ox = 40;
				Ca = 15;
				Ni = 25;
				Hy = 65;
				Bk = 100;
				for (int i = 0; i < cols; i++) {
					for (int j = 0; j < rows; j++) {
						//spawn new atom based on i,j position. 
						//Spawn from the atoms:Blank atom,hydrogen,oxygen,nitrogen,carbon
						spawnNewAtom(i, j, Spawn(Bk, Hy, Ox, Ni, Ca));
					}
				}
				Ox = 30;
				Ca = 5;
				Ni = 10;
				Hy = 100;
				oldNumAtoms = numAtoms;
				
				//setting timer to count up in hard mode screen.
				timer.setDirection(Timer.COUNTUP);
				timer.start(LIMIT);
				redraw();
			}
			// Instructions Button
			if (atomOver(30 + (199 / 2), 450 + (199 / 2), 199)) {
				state = STATE_INSTRUCTIONS;
				redraw();
			}
			// High Scores Button
			if (atomOver(257 + (199 / 2), 567 + (199 / 2), 199)) {
				state = STATE_HIGHSCORE;
				redraw();
			}
		}
		if (state == STATE_PAUSED) {
			printToLog("   [RESUME] Game resumed");
			// If player touches screen, game resumes
			timer.resume();
			state = prevState;
			redraw();
		}
		//Screen mode either being in hard or easy mode.
		//Same rules apply, pause button will pause game
		//Main Menu button will end current game and send the player to the Main Menu page.
		if (state == STATE_TIMED || state == STATE_STRATEGY) {
			// Pause Button
			if (overRect(20, 706, 200, 64)) {
				printToLog("[PAUSED] Timer: " + timer.getTime() + ", Score: "
						+ CurScore);
				prevState = state;
				timer.pause();
				state = STATE_PAUSED;
				redraw();
			}
			// Main Menu Button
			if (overRect(260, 706, 200, 64)) {
				printToLog("   [END] Player ends game");
				timer.pause();
				state = STATE_WELCOME;
				redraw();
				grid = null;
				history = null;

				System.gc(); // for garbage collection
			}
		}
		//Game over screen
		if (state == STATE_GAMEOVER) {
			// Hi Scores Button
			if (overRect(20, 463, 200, 64)) {
				timer.pause();
				state = STATE_HIGHSCORE;
				redraw();
				System.gc(); // for garbage collection
			}
			// Main Menu Button
			if (overRect(260, 463, 200, 64)) {
				timer.pause();
				state = STATE_WELCOME;
				redraw();
				System.gc(); // for garbage collection
			}
		}
		//Instructions screen
		if (state == STATE_INSTRUCTIONS) {
			// Details Button
			if (overRect(61, 644, 362, 75)) {
				state = STATE_INSTRUCTIONS2;
				redraw();
			}
			// Main Menu Button
			if (overRect(147, 724, 182, 46)) {
				state = STATE_WELCOME;
				redraw();
			}

		}
		//Instructions screen part 2
		if (state == STATE_INSTRUCTIONS2) {
			// Main Menu Button
			if (overRect(62, 680, 362, 75)) {
				state = this.STATE_INSTRUCTIONS3;
				redraw();
			}
		}
		
		//instructions part 3
		if (state == STATE_INSTRUCTIONS3) {
			// Main Menu Button
			if (overRect(147, 724, 182, 46)) {
				state = this.STATE_WELCOME;
				redraw();
			}
		}
		
		//High scores screen
		if (state == STATE_HIGHSCORE) {
			// Main Menu Button
			if (overRect(147, 724, 182, 46)) {
				state = STATE_WELCOME;
				redraw();
			}
		}
		return false;
	}

	// ANDROID
	// Fired when the user is touching the screen
	// Only in game modes
	// -------is continuous-------
	public boolean onTouchEvent(MotionEvent event) {
		if (state == STATE_TIMED || state == STATE_STRATEGY) {
			return listener.onTouchEvent(event);
		} else
			return false;
	}

	// Sets the selectedAtom to the atom the user clicks on
	// DOES NOT set or break bonds
	public void singleTap() {
		Atom temp = null;
		boolean found = false;

		for (int i = 0; i < cols && !found; i++) {
			for (int j = 0; j < rows && !found; j++) {
				temp = grid[i][j];

				if (!(temp instanceof blankAtom)
						&& atomOver(temp.xChord, temp.yChord, temp.size)) {
					found = true;
				}

			}
		}

		if (found && selectedAtom != null && selectedAtom != temp) {
			selectedAtom.highlight = false;
			selectedAtom = temp;
			selectedAtom.highlight = true;

		} else if (found && selectedAtom == null) {
			selectedAtom = temp;
			selectedAtom.highlight = true;
			//selecting a blank atom, do nothing
		} else if (selectedAtom != null) {
			selectedAtom.highlight = false;
			selectedAtom = null;
		}
	}

	// ANDROID
	// Fires when user performs a swipe motion on screen
	// Swaps atoms and checks for completed molecules
	public void swipe(int direction) {
		if (selectedAtom == null)
			return;
		Atom temp = null;

		switch (direction) {
		case GestureListener.SWIPE_UP://swipe up listener
			if (selectedAtom.jth > 0) {//checking where the atom is in the grid
				temp = grid[selectedAtom.ith][selectedAtom.jth - 1];//atom goes up. 
				//print out the atom it swapped position's with.
				printToLog("   [MOVE] Player selects "
						+ selectedAtom.toString()
						+ ", swipes UP, swapping places with "
						+ temp.toString());
			}
			break;
		case GestureListener.SWIPE_DOWN://swipe down listener
			if (selectedAtom.jth < rows - 1) {//checking where the atom is in the grid
				temp = grid[selectedAtom.ith][selectedAtom.jth + 1];//atom goes down. 
				printToLog("   [MOVE] Player selects "
						+ selectedAtom.toString()
						+ ", swipes DOWN, swapping places with "
						+ temp.toString());
			}
			break;
		case GestureListener.SWIPE_LEFT://swipe left listener
			if (selectedAtom.ith > 0) {//checking where the atom is in the grid
				temp = grid[selectedAtom.ith - 1][selectedAtom.jth];//atom goes left. 
				printToLog("   [MOVE] Player selects "
						+ selectedAtom.toString()
						+ ", swipes LEFT, swapping places with "
						+ temp.toString());
			}
			break;
		case GestureListener.SWIPE_RIGHT://swipe right listener
			if (selectedAtom.ith < cols - 1) {//checking where the atom is in the grid
				temp = grid[selectedAtom.ith + 1][selectedAtom.jth];//atom goes right. 
				printToLog("   [MOVE] Player selects "
						+ selectedAtom.toString()
						+ ", swipes RIGHT, swapping places with "
						+ temp.toString());
			}
			break;

		default:
			System.out.println("Direction not recognized");
		}

		if (temp != null) {
			breakAtom(temp);
			breakAtom(selectedAtom);

			// Switch in the grid
			grid[temp.ith][temp.jth] = grid[selectedAtom.ith][selectedAtom.jth];
			grid[selectedAtom.ith][selectedAtom.jth] = temp;

			// Switch graphically here
			float tempX = selectedAtom.xChord;
			float tempY = selectedAtom.yChord;
			int tempI = selectedAtom.ith;
			int tempJ = selectedAtom.jth;

			selectedAtom.xChord = temp.xChord;
			selectedAtom.yChord = temp.yChord;
			selectedAtom.ith = temp.ith;
			selectedAtom.jth = temp.jth;

			temp.xChord = tempX;
			temp.yChord = tempY;
			temp.ith = tempI;
			temp.jth = tempJ;

			setAtom(temp);
			setAtom(selectedAtom);

			processMolecule(selectedAtom.molecule);
			if (!(temp instanceof blankAtom)
					&& selectedAtom.molecule != temp.molecule)
				processMolecule(temp.molecule);
		}
		atomSpawnChance();
		ifGameOver();
	}

	// ANDROID
	// Makes bonds to all adjacent atoms
	// Does not move atoms
	public void doubleTap() {
		Atom temp = null;
		boolean found = false;

		//if atoms are adjacent to the double tapped atom create bond.
		for (int i = 0; i < cols && !found; i++) {
			for (int j = 0; j < rows && !found; j++) {
				temp = grid[i][j];

				if (!(temp instanceof blankAtom)
						&& atomOver(temp.xChord, temp.yChord, temp.size)) {
					found = true;
				}
			}
		}

		if (found) {
			breakAtom(temp);
			setAtom(temp);
			processMolecule(temp.molecule);
			atomSpawnChance();

			ifGameOver();
		}

	}

	// The order of bonding goes as follows:
	// up, n.east, right, s.east, down, s.west, left, n.west
	private void setAtom(Atom atom) {
		if (atom instanceof blankAtom)
			return;

		if (atom.jth > 0)
			atom.makeBond(grid[atom.ith][atom.jth - 1]);// up
		if ((atom.ith < cols - 1) && (atom.jth > 0))
			atom.makeBond(grid[atom.ith + 1][atom.jth - 1]);// n.east
		if (atom.ith < cols - 1)
			atom.makeBond(grid[atom.ith + 1][atom.jth]); // right
		if ((atom.ith < cols - 1) && (atom.jth < rows - 1))
			atom.makeBond(grid[atom.ith + 1][atom.jth + 1]);// s.east
		if (atom.jth < rows - 1)
			atom.makeBond(grid[atom.ith][atom.jth + 1]);// down
		if ((atom.ith > 0) && (atom.jth < rows - 1))
			atom.makeBond(grid[atom.ith - 1][atom.jth + 1]);// s.west
		if (atom.ith > 0)
			atom.makeBond(grid[atom.ith - 1][atom.jth]);// left
		if ((atom.ith > 0) && (atom.jth > 0))
			atom.makeBond(grid[atom.ith - 1][atom.jth - 1]);// n.west
	}

	// Breaks atom bonds
	// The order goes as follows:
	// up, n.east, right, s.east, down, s.west, left, n.west
	private void breakAtom(Atom atom) {
		if (atom instanceof blankAtom)
			return;

		if (atom.jth > 0)
			atom.breakBond(grid[atom.ith][atom.jth - 1]);// up
		if ((atom.ith < cols - 1) && (atom.jth > 0))
			atom.breakBond(grid[atom.ith + 1][atom.jth - 1]);// n.east
		if (atom.ith < cols - 1)
			atom.breakBond(grid[atom.ith + 1][atom.jth]); // right
		if ((atom.ith < cols - 1) && (atom.jth < rows - 1))
			atom.breakBond(grid[atom.ith + 1][atom.jth + 1]);// s.east
		if (atom.jth < rows - 1)
			atom.breakBond(grid[atom.ith][atom.jth + 1]);// down
		if ((atom.ith > 0) && (atom.jth < rows - 1))
			atom.breakBond(grid[atom.ith - 1][atom.jth + 1]);// s.west
		if (atom.ith > 0)
			atom.breakBond(grid[atom.ith - 1][atom.jth]);// left
		if ((atom.ith > 0) && (atom.jth > 0))
			atom.breakBond(grid[atom.ith - 1][atom.jth - 1]);// n.west
	}

	// Determines if the mouse is over an atom
	// Returns true if it is, and false otherwise
	private boolean atomOver(float x, float y, float diameter) {
		float disX = x - this.mouseX;
		float disY = y - this.mouseY;
		if (sqrt(sq(disX) + sq(disY)) < diameter / 2) {
			return true;
		} else {
			return false;
		}

	}

	// Returns a value 0-4 with certain probabilities
	private int Spawn(int B, int H, int O, int N, int C) {
		// Changed the array size from 100 because the sum of all
		// the numbers in the parameter may not equal 100
		int[] Chance = new int[B + H + O + N + C];
		int i, result;
		int j = 0;

		for (i = 0; i < B; i++) {
			Chance[j] = 0;
			j++;
		}
		for (i = 0; i < H; i++) {
			Chance[j] = 1;
			j++;
		}
		for (i = 0; i < O; i++) {
			Chance[j] = 2;
			j++;
		}
		for (i = 0; i < N; i++) {
			Chance[j] = 3;
			j++;
		}
		for (i = 0; i < C; i++) {
			Chance[j] = 4;
			j++;
		}

		result = rand.nextInt(B + H + O + N + C);

		return Chance[result];

	}

	// Creates a new atom in the grid at indices i and j
	// with the third parameter determining which atom will be created
	private void spawnNewAtom(int i, int j, int determinate) {
		numAtoms++;
		switch (determinate) {
		case 1:
			grid[i][j] = new Hydrogen(i * sz + (sz / 2), j * sz + (sz / 2), i,
					j);
			break;
		case 2:
			grid[i][j] = new Oxygen(i * sz + (sz / 2), j * sz + (sz / 2), i, j);
			break;
		case 3:
			grid[i][j] = new Nitrogen(i * sz + (sz / 2), j * sz + (sz / 2), i,
					j);
			break;
		case 4:
			grid[i][j] = new Carbon(i * sz + (sz / 2), j * sz + (sz / 2), i, j);
			break;

		default:
			grid[i][j] = new blankAtom(i * sz + (sz / 2), j * sz + (sz / 2), i,
					j);
			numAtoms--;
		}
	}

	// Erases molecules, adding molecules to the history array
	// Gives points based on the molecule created
	// Adjusts difficulty
	private void processMolecule(Molecule molecule) {
		int score = 0;
		int bonus = 0;
		boolean newMolecule = true;
		//if statement checking if molecule is complete and printing that it is a complete molecule.
		if (molecule.checkCompleteness()) {
			printToLog("   [COMPLETED MOLECULE] " + molecule.toString());

			score = molecule.eraseMolecule(grid, sz);// erases the molecule

			numAtoms -= molecule.count;
			System.out.println("Subtracting from count: " + molecule.count);

			// add molecule to the history list if it does not equal to any of
			// the molecules
			// in the history. that way we don't have "repeat" molecules in the
			// history
			Iterator<Molecule> itr = history.iterator();
			Molecule tempMolecule;
			while (itr.hasNext() && newMolecule) {
				tempMolecule = itr.next();
				if (molecule.compareTo(tempMolecule))
					newMolecule = false;
			}
			// Adds new molecules to the history array
			if (newMolecule) {
				history.add(molecule);
				bonus = score / 2;

				printToLog("   [NEW MOLECULE] History:");
				int k = 0;
				itr = history.iterator();
				while (itr.hasNext()) {
					k++;
					tempMolecule = itr.next();
					printToLog("      [" + k + "] " + tempMolecule.toString());
				}
				printToLog("      Total Completed Molecules: " + k);
			}

			// Vibrates the phone
			vibrate.vibrate(500);
			System.out.println("VIBRATOR: phone vibrated for 500 milliseconds");

			// Modifies game difficulty in easy mode
			if (state == STATE_STRATEGY) {
				if (newMolecule) {
					if (molecule.count == 5)
						bonus = score / 2;
					else if (molecule.count > 5)
						bonus = (int) (score * (molecule.count / 10.0));
					CurBonus += bonus;
					CurScore += score += bonus;
				}

				oldNumAtoms = numAtoms;
				//the increase of difficulty depends on user score
				//less spawning of new atoms and the probability of each atom decreases. 

				if (CurScore <= 1000 && CurScore > 2000) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 8;
					Ox = 45;
					Hy = 120;
					Ca = 10;
					Ni = 25;
				} else if (CurScore <= 2000 && CurScore < 3500) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 6;
					Ox = 45;
					Hy = 80;
					Ca = 15;
					Ni = 30;
				} else if (CurScore <= 3500 && CurScore < 5500) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 5;
					Ox = 45;
					Hy = 50;
					Ca = 20;
					Ni = 30;
				} else if (CurScore <= 5500 && CurScore < 8000) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 4;
					Ox = 40;
					Hy = 40;
					Ca = 30;
					Ni = 40;
				} else if (CurScore <= 8000 && CurScore < 11000) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 3;
					Ox = 35;
					Hy = 30;
					Ca = 30;
					Ni = 40;
				} else if (CurScore >= 15000) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 2;
					Ox = 30;
					Hy = 20;
					Ca = 40;
					Ni = 50;
				}
				
				// Modifies game difficulty in hard mode.
			} else if (state == STATE_TIMED) {
				if (molecule.count == 5)
					bonus = score;
				else if (molecule.count > 5)
					bonus = (int) (score * (molecule.count / 5.0));
				if (newMolecule)
					bonus *= 2;
				CurBonus += bonus;
				CurScore += score += bonus;
				printToLog("   [EVENT] " + (ADDTIME / 1000.0)
						+ " seconds added to the timer");
				timer.addtime(ADDTIME);
				
				//the increase of difficulty depends on user score
				//less spawing of new atoms and the probability of each atom decreases. 
				if (CurScore <= 1000 && CurScore > 2000) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 8;
					Ox = 45;
					Hy = 120;
					Ca = 10;
					Ni = 25;
				} else if (CurScore <= 2000 && CurScore < 4000) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 6;
					Ox = 45;
					Hy = 80;
					Ca = 15;
					Ni = 30;
				} else if (CurScore <= 4000 && CurScore < 8000) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 5;
					Ox = 45;
					Hy = 50;
					Ca = 20;
					Ni = 30;
				} else if (CurScore <= 8000 && CurScore < 15000) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 4;
					Ox = 40;
					Hy = 40;
					Ca = 30;
					Ni = 40;
				} else if (CurScore <= 15000 && CurScore < 20000) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 3;
					Ox = 35;
					Hy = 30;
					Ca = 30;
					Ni = 40;
				} else if (CurScore >= 20000) {
					printToLog("   [EVENT] Difficulty increased");
					spawnAtom = 2;
					Ox = 30;
					Hy = 20;
					Ca = 40;
					Ni = 50;
				}
			}

			System.out.println("Bonus = " + bonus + "  Score is " + CurScore);
			printToLog("   [SCORE] Bonus = " + bonus + ", Score is " + CurScore
					+ "; Atoms left = " + numAtoms + "; Time: "
					+ timer.getTime());
			//if statement checking the current score with the highest score over all.
			//if current score is higher then higher then high score will be replaced.
			if (CurScore >= HiScore) {
				printToLog("   [NEW HIGH SCORE] Replacing previous high score of "
						+ HiScore + " with player score of " + CurScore);
				HiScore = CurScore;
			}
		}
	}

	// Determines if the mouse is over a menu button
	private boolean overRect(int x, int y, int width, int height) {
		if (mouseX >= x && mouseX <= x + width && mouseY >= y
				&& mouseY <= y + height) {
			return true;
		} else {
			return false;
		}
	}

	// Determines if a game over requirement has been met
	private void ifGameOver() {
		System.out.println("numAtoms = " + numAtoms);

		boolean gameEnding = false;
		//if statement checking in hard mode
		if (state == STATE_TIMED) {
			//if no more atoms on grid *Game Over*
			if (numAtoms <= 0) {
				printToLog("   [EVENT] Game board is EMPTY");
				gameEnding = true;
				
			//if grid is full and no more atoms are able to be inserted onto grid *Game Over*
			} else if (numAtoms == cols * rows) {
				printToLog("   [EVENT] Game board is FULL");
				gameEnding = true;
				
			//if there are less than 4 atoms on the grid, then more will be generated.
			} else if (numAtoms <= 4) {
				printToLog("   [EVENT] Game board has <= 4 atoms left; Generating new atoms");
				int H = 0, O = 0, N = 0, C = 0;
				Atom temp;

				for (int i = 0; i < cols; i++) {
					for (int j = 0; j < rows; j++) {
						temp = grid[i][j];
						if (temp instanceof Oxygen)
							O++;
						else if (temp instanceof Hydrogen)
							H++;
						else if (temp instanceof Carbon)
							C++;
						else if (temp instanceof Nitrogen)
							N++;
					}
				}

				System.out.println("checking < 5");
				if (H <= 2 || O <= 2 || C != 3 || (N != 2 && O != 1)
						|| (N != 1 && H != 1 && 0 != 1)
						|| (C != 2 && N != 1 && O != 1)) {
					int tempNum = numOfMoves;
					for (int i = 0; i < 4; i++) {
						numOfMoves = spawnAtom;
						atomSpawnChance();
					}
					numOfMoves = tempNum;
				}
			//if timer runs out in hard mode.
			} else if (timer.getTime() == 0) {
				printToLog("   [EVENT] Timer has reached 0");
				gameEnding = true;
			}
			
		//Game over options in easy mode.
		} else if (state == STATE_STRATEGY) {
			//if no more atoms on grid *Game Over*
			if (numAtoms <= 0) {
				printToLog("   [EVENT] Game board is EMPTY");
				gameEnding = true;
			}
			// If 5 new atoms are created before a new molecule is made *Game Over*
			else if (numAtoms - oldNumAtoms == 5) {
				printToLog("   [EVENT] 5 new atoms have been created since last molecule");
				gameEnding = true;
			//if there are less than 4 atoms on the grid, then more will be generated.	
			} else if (numAtoms <= 4) {
				printToLog("   [EVENT] Game board has <= 4 atoms left; Generating new atoms");
				int H = 0, O = 0, N = 0, C = 0;
				Atom temp;

				for (int i = 0; i < cols; i++) {
					for (int j = 0; j < rows; j++) {
						temp = grid[i][j];
						if (temp instanceof Oxygen)
							O++;
						else if (temp instanceof Hydrogen)
							H++;
						else if (temp instanceof Carbon)
							C++;
						else if (temp instanceof Nitrogen)
							N++;
					}
				}

				System.out.println("checking < 5");
				if (H <= 2 || O <= 2 || C != 3 || (N != 2 && O != 1)
						|| (N != 1 && H != 1 && 0 != 1)
						|| (C != 2 && N != 1 && O != 1))
					gameEnding = true;
			}
		}
		//if statement checking that the *Game Over* = true and there are still atoms onto grid. 
		//then add those atoms onto deductions for score purposes.
		if (gameEnding && numAtoms != 0) {
			Atom temp;
			for (int i = 0; i < cols; i++) {
				for (int j = 0; j < rows; j++) {
					temp = grid[i][j];
					if (!(temp instanceof blankAtom))
						deductions += temp.points;
				}
			}
		}
		
		// If a game over requirement is met,
		//print statements  showing user scores, bonuses, deductions, etc.
		if (gameEnding) {
			grid = null;
			history = null;
			System.gc(); // garbage collection flag
			state = STATE_GAMEOVER;
			System.out.println("--------------GAMEOVER--------------");
			System.out.println("Score: " + (CurScore - CurBonus) + "\nBonus: "
					+ CurBonus + "\nDeductions: " + deductions);

			printToLog("   [GAME OVER] Time: " + timer.getTime());
			printToLog("               Score: " + (CurScore - CurBonus)
					+ "\n               Bonus: " + CurBonus
					+ "\n               Deductions: " + deductions);

			CurScore -= deductions;

			if (CurScore >= HiScore) {
				HiScore = CurScore;
				printToLog("   [NEW HIGH SCORE] " + HiScore);
				//SaveScores(HiScore, scores);
			}

		}
	}

	// Saves high score values to the SCORES file
	private String[] SaveScores(int HiScore, String[] scores,String[] ref) {
		String s1;
		String s2;
		int temp = -1;
		if (scores.length > 0) {
			s2 = scores[scores.length-1];
			
			//Score does not break list of top scores
			if(HiScore < parseInt(s2)){
				
				return scores;
				
			}
			
			// else find the largest score that current HiScore is greater than. 
			for(i=0;i<scores.length;i++){
				
				s1=scores[i];
				
				if (HiScore > parseInt(s1)){
					temp = i;
					break;
				}
			}
			if(temp!=-1){
				
				for(i =temp; i<scores.length-1; i++){
					scores[i+1] = ref[i];
 				
				}
			}
				
			scores[temp] = Integer.toString(HiScore);
				saveStrings("SCORES", scores);
			// If HiScore is a top score but not THE top score
			// then search through to find index of first score greater than
			saveStrings("SCORES", scores);
		}

		return scores;

	}
	
	private void SaveScores(int highScore){	
		//inserts the highscore and shifts all lower scores down by 1
		for(int k=0; k < scores.length; k++){
			if(highScore > parseInt(scores[k])){
				for(int j=scores.length-1; j > k; j--)
					scores[j] = scores[j-1];
				scores[k] = ""+highScore;
				break;
			}
				
		}
		
		HiScore = parseInt(scores[scores.length-1]);//the lowest of the highscores
		
		//does not need to write the final scores untill the app closes!
	}

	// Determines what type of atom is spawned on the board next
	private void atomSpawnChance() {
		numOfMoves++;
		if (numOfMoves >= spawnAtom) {
			// if statement checking if grid is already full with atoms then don't create a new atom 
			if (numAtoms < cols * rows) {
				int ith = rand.nextInt(8);
				int jth = rand.nextInt(8);
				while (!(grid[ith][jth] instanceof blankAtom)) {
					ith = rand.nextInt(8);
					jth = rand.nextInt(8);
				}
				//adding a new atom onto screen
				spawnNewAtom(ith, jth, Spawn(0, Hy, Ox, Ni, Ca));
			}
			numOfMoves = 0;
		}
	}

	// Prints the current and high scores on the game board
	private void refreshScores() {
		PFont font;
		font = loadFont("Roboto-Bold-48.vlw");
		fill(255);
		textFont(font);
		textAlign(PConstants.LEFT);
		textSize(22);
		text(CurScore, 198, 629);
		text(HiScore, 198, 678);
	}

	// Requires enabling android permissions:
	// android.permission.WRITE_EXTERNAL_STORAGE
	private void initLog(String filename) {
		try {
			log = createWriter(filename);
			log.println("== NEW SESSION ==");
			log.flush();
		} catch (Exception e) {
			System.out.println("Error with log file.");
		}
	}

	// Used to print the message to the player log
	private void printToLog(String string) {
		log.println(string);
		log.flush();
	}

	// Called when the app is closed
	public void onStop() {
		// Closes player log
		if (log != null) {
			log.println("== END SESSION ==");
			log.flush();
			log.close();
		}

		// Stops music when app closes
		main.stop();
		timed.stop();
		strategy.stop();
		super.onStop(); // Call onDestroy on super class
		System.exit(0); // Shutdown game
	}

	public void onPause() {
		super.onPause();
		
		// Pauses music when app pauses
			timed.pause();
			strategy.pause();
			main.pause();
			
			isPaused = true;
	}

	public void onResume() {
		super.onResume();
		
		// Restarts music when app resumes
		if (state == STATE_TIMED && isPaused == true){
			isPaused = false;
			timed.start();
		}
				
				
		if (state == STATE_STRATEGY && isPaused == true){
			isPaused = false;
			strategy.start();
		}
		else if(isPaused){
			isPaused = false;;
			main.start();
		}
	}
}
